package com.example.geoaimavlink_android.utils;

import android.content.Context;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.geoai.mavlink.geoainet.airlink.info.CellularQualityInfo;
import com.geoai.mavlink.geoainet.airlink.info.CellularSignalInfo;
import com.geoai.mavlink.geoainet.airlink.info.K3QButtonHardwareInfo;
import com.geoai.mavlink.geoainet.airlink.interfaces.IAntennaSignalListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IBaseRemoteControllerListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.ICellularSignalLevelListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.ICellularVideoQualityListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IK3QHardwareStateListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IK3QRemoteControllerListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IBaseCellularListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IK3RRemoteControllerListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IWirelessDataTransmissionListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IWirelessSignalListener;
import com.geoai.mavlink.geoainet.airlink.AirlinkManager;
import com.geoai.mavlink.geoainet.airlink.interfaces.IBaseDspListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IBigFishDspListener;
import com.geoai.mavlink.geoainet.airlink.interfaces.IP301DDspListener;
import com.geoai.mavlink.geoainet.banfly.BanFlyManager;
import com.geoai.mavlink.geoainet.banfly.interfaces.IBanFlyManager;
import com.geoai.mavlink.geoainet.base.mavlinkcore.controller.StarLinkClientManager;
import com.geoai.mavlink.geoainet.base.mavlinkcore.engine.GEOAIError;
import com.geoai.mavlink.geoainet.base.mavlinkcore.interfaces.CompletionCallback;
import com.geoai.mavlink.geoainet.battery.info.BatteryStateInfo;
import com.geoai.mavlink.geoainet.battery.interfaces.IBatteryManager;
import com.geoai.mavlink.geoainet.battery.interfaces.IBatteryStateListener;
import com.geoai.mavlink.geoainet.crest.interfaces.ICrestManager;
import com.geoai.mavlink.geoainet.crest.interfaces.ILteHardwareStateListener;
import com.geoai.mavlink.geoainet.flycontroller.interfaces.IBaseFlyControllerManager;
import com.geoai.mavlink.geoainet.flycontroller.interfaces.IDiagnosticsStateListener;
import com.geoai.mavlink.geoainet.flycontroller.interfaces.IFlyControllerStateListener;
import com.geoai.mavlink.geoainet.flycontroller.interfaces.IParachuteStateListener;
import com.geoai.mavlink.geoainet.general.interfaces.IGeneralManager;
import com.geoai.mavlink.geoainet.general.interfaces.IJetsonHardwareStateListener;
import com.geoai.mavlink.geoainet.gimbal.interfaces.IBaseGimbalManager;
import com.geoai.mavlink.geoainet.gimbal.interfaces.IGimbalStateListener;
import com.geoai.mavlink.geoainet.media.interfaces.IBaseMediaManager;
import com.geoai.mavlink.geoainet.mission.enums.MissionState;
import com.geoai.mavlink.geoainet.mission.interfaces.IBaseMissionManager;
import com.geoai.mavlink.geoainet.mission.interfaces.IMissionStateListener;
import com.geoai.mavlink.geoainet.obstacle.interfaces.IObstacleManager;
import com.geoai.mavlink.geoainet.obstacle.interfaces.IObstacleStateListener;
import com.geoai.mavlink.geoainet.payload.info.ThermalAreaMeasureInfo;
import com.geoai.mavlink.geoainet.payload.interfaces.ICameraCaptureInfoListener;
import com.geoai.mavlink.geoainet.payload.interfaces.ICameraInformationListener;
import com.geoai.mavlink.geoainet.payload.interfaces.ICameraNewGeneratedInfoListener;
import com.geoai.mavlink.geoainet.payload.interfaces.ICameraStorageInfoListener;
import com.geoai.mavlink.geoainet.payload.interfaces.INV2CameraListener;
import com.geoai.mavlink.geoainet.payload.interfaces.INV3CameraListener;
import com.geoai.mavlink.geoainet.payload.interfaces.ISS125SpeakerListener;
import com.geoai.mavlink.geoainet.payload.interfaces.IThermalAreaMeasureListener;
import com.geoai.mavlink.geoainet.payload.module.camera.NV2Camera;
import com.geoai.mavlink.geoainet.payload.module.camera.NV3Camera;
import com.geoai.mavlink.geoainet.payload.module.speaker.SS125Speaker;
import com.geoai.mavlink.geoainet.rtk.interfaces.IBaseRtkManager;
import com.geoai.mavlink.geoainet.rtk.interfaces.IGpsSatelliteInfoListener;
import com.geoai.mavlink.geoainet.rtk.interfaces.IRtkStateListener;
import com.geoai.mavlink.geoainet.system.interfaces.IBaseSystemManager;
import com.google.gson.Gson;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ApiSelectUtils {

    private static ApiSelectUtils mInstance;

    public static ApiSelectUtils getInstance() {
        if (mInstance == null) {
            synchronized (Object.class) {
                if (mInstance == null) {
                    mInstance = new ApiSelectUtils();
                }
            }
        }
        return mInstance;
    }

    private final Gson mGson = new Gson();

    private boolean isJsonFormatted = false;

    private InvokeCallback mCallback;

    /**
     * 获取组件的类目
     *
     * @param s method name
     * @return *.clazz
     */
    public Method[] getApi(String s) {
        switch (s) {
            case "StarLinkClientManager":
                Method[] methods = StarLinkClientManager.class.getMethods();
                List<Method> methodList = new ArrayList<>();
                for (Method method : methods) {
                    if (method.getModifiers() == 1 && method.getReturnType().getName().equals("void")) {
                        methodList.add(method);
                    }
                }
                return methodList.toArray(new Method[0]);
            case "FlyController":
                return IBaseFlyControllerManager.class.getMethods();
            case "Cellular":
                return IBaseCellularListener.class.getMethods();
            case "AirLink-BigFish":
                return IBigFishDspListener.class.getMethods();
            case "AirLink-P301":
                return IP301DDspListener.class.getMethods();
            case "RC-K3Q":
                return IK3QRemoteControllerListener.class.getMethods();
            case "RC-K3R":
                return IK3RRemoteControllerListener.class.getMethods();
            case "Battery":
                return IBatteryManager.class.getMethods();
            case "SS125":
                return ISS125SpeakerListener.class.getMethods();
            case "Camera-Y2":
                return INV2CameraListener.class.getMethods();
            case "Camera-Y3":
                return INV3CameraListener.class.getMethods();
            case "Gimbal":
                return IBaseGimbalManager.class.getMethods();
            case "System":
                return IBaseSystemManager.class.getMethods();
            case "RTK":
                return IBaseRtkManager.class.getMethods();
            case "General":
                return IGeneralManager.class.getMethods();
            case "Mission":
                return IBaseMissionManager.class.getMethods();
            case "Crest":
                return ICrestManager.class.getMethods();
            case "Media":
                return IBaseMediaManager.class.getMethods();
            case "BanFly":
                return IBanFlyManager.class.getMethods();
            case "Obstacle":
                return IObstacleManager.class.getMethods();
            default:
                return null;
        }
    }

    public Object getApiImplementation(String s, int droneID) {
        if (s.equals("StarLinkClientManager")) {
            return StarLinkClientManager.getInstance();
        } else if (s.equals("BanFly")) {
            return BanFlyManager.getInstance();
        } else if (s.equals("AirLink-BigFish")) {
            IBaseDspListener dsp = AirlinkManager.getInstance().getDsp();
            if (dsp instanceof IBigFishDspListener) {
                return dsp;
            } else {
                return null;
            }
        } else if (s.equals("AirLink-P301")) {
            IBaseDspListener dsp = AirlinkManager.getInstance().getDsp();
            if (dsp instanceof IP301DDspListener) {
                return dsp;
            } else {
                return null;
            }
        } else if (s.equals("RC-K3Q")) {
            IBaseRemoteControllerListener rc = AirlinkManager.getInstance().getRc();
            if (rc instanceof IK3QRemoteControllerListener) {
                return rc;
            } else {
                return null;
            }
        } else if (s.equals("RC-K3R")) {
            IBaseRemoteControllerListener rc = AirlinkManager.getInstance().getRc();
            if (rc instanceof IK3RRemoteControllerListener) {
                return rc;
            } else {
                return null;
            }
        } else if (s.equals("Cellular")) {
            return AirlinkManager.getInstance().getCellular();
        }
        if (StarLinkClientManager.getInstance().getProduct(droneID) == null) return null;
        switch (s) {
            case "FlyController":
                return StarLinkClientManager.getInstance().getProduct(droneID).getFlyControllerManager();
            case "AirLink":
                return StarLinkClientManager.getInstance().getProduct(droneID).getAirLinkManager();
            case "Battery":
                return StarLinkClientManager.getInstance().getProduct(droneID).getBatteryManager();
            case "SS125":
                return (SS125Speaker) StarLinkClientManager.getInstance().getProduct(droneID).getPayloadManager().getSpeaker();
            case "Camera-Y2":
                return (NV2Camera) StarLinkClientManager.getInstance().getProduct(droneID).getPayloadManager().getCamera();
            case "Camera-Y3":
                return (NV3Camera) StarLinkClientManager.getInstance().getProduct(droneID).getPayloadManager().getCamera();
            case "Gimbal":
                return StarLinkClientManager.getInstance().getProduct(droneID).getGimbalManager();
            case "System":
                return StarLinkClientManager.getInstance().getProduct(droneID).getSystemManager();
            case "RTK":
                return StarLinkClientManager.getInstance().getProduct(droneID).getRtkManager();
            case "General":
                return StarLinkClientManager.getInstance().getProduct(droneID).getGeneralManager();
            case "Mission":
                return StarLinkClientManager.getInstance().getProduct(droneID).getMissionManager();
            case "Crest":
                return StarLinkClientManager.getInstance().getProduct(droneID).getCrestManager();
            case "Media":
                return StarLinkClientManager.getInstance().getProduct(droneID).getMediaManager();
            case "Obstacle":
                return StarLinkClientManager.getInstance().getProduct(droneID).getObstacleManager();
            default:
                return null;
        }
    }

    public boolean isParamNeedInput(Method method) {
        int paramCount = getMethodParamCount(method);
        if (paramCount == 1 && !isEndParamInterface(method)) {
            return true;
        } else if (paramCount == 2 && isEndParamInterface(method)) {
            return true;
        }
        return false;
    }

    public boolean isSingleParam(Method method) {
        int paramCount = getMethodParamCount(method);
        switch (paramCount) {
            case 0: // 没有参数直接调用
                return true;
            case 1: // 只有一个参数，并且最后一个参数是接口类型
                return isEndParamInterface(method);
            default:
                return false;
        }
    }

    public boolean isEndParamCallback(Method method) {
        if (!isEndParamInterface(method)) {
            return false;
        }
        String methodName = method.getParameterTypes()[method.getParameterTypes().length - 1].getName();
        String targetName1 = CompletionCallback.ICompletionCallback.class.getName();
        String targetName2 = CompletionCallback.ICompletionCallbackWithParam.class.getName();
        String targetName3 = CompletionCallback.ICompletionCallbackWithString.class.getName();
        String targetName4 = CompletionCallback.ICompletionCallbackWithProcess.class.getName();
        String targetName5 = CompletionCallback.ICompletionCallbackWith.class.getName();
        return methodName.equals(targetName1) ||
                methodName.equals(targetName2) ||
                methodName.equals(targetName3) ||
                methodName.equals(targetName4) ||
                methodName.equals(targetName5);
    }

    public Object getParamInvoke(String paramName, String paramValue) {
        switch (paramName) {
            case "java.lang.Long":
                return Long.parseLong(paramValue);
            case "boolean":
                return Boolean.parseBoolean(paramValue);
            case "int":
            case "java.lang.Integer":
                return Integer.parseInt(paramValue);
            case "float":
                return Float.parseFloat(paramValue);
            case "double":
                return Double.parseDouble(paramValue);
            case "java.lang.String":
                return paramValue;
        }
        return null;
    }

    public Object getInterfaceInvoke(String paramName) {
        if (paramName.contains("IFlyControllerStateListener")) {
            return (IFlyControllerStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IParachuteStateListener")) {
            return (IParachuteStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IDiagnosticsStateListener")) {
            return (IDiagnosticsStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IBatteryStateListener")) {
            return (IBatteryStateListener) aircraftBatteryInfo -> {
                if (mCallback != null) {
                    for (BatteryStateInfo batteryStateInfo : aircraftBatteryInfo) {
                        mCallback.onResult(mGson.toJson(batteryStateInfo));
                    }
                }
            };
        } else if (paramName.contains("IJetsonHardwareStateListener")) {
            return (IJetsonHardwareStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("ILteHardwareStateListener")) {
            return (ILteHardwareStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IGimbalStateListener")) {
            return (IGimbalStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IRtkStateListener")) {
            return (IRtkStateListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("INtripServerConnectListener")) {
//            return (INtripServerConnectListener) code -> {
//                if (mCallback != null) mCallback.onResult(format(mGson.toJson(code)));
//            };
        } else if (paramName.contains("IAntennaSignalListener")) {
            return (IAntennaSignalListener) code -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(code)));
            };
        } else if (paramName.contains("IWirelessDataTransmissionListener")) {
            return (IWirelessDataTransmissionListener) code -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(code)));
            };
        } else if (paramName.contains("IWirelessSignalListener")) {
            return (IWirelessSignalListener) code -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(code)));
            };
        } else if (paramName.contains("IObstacleStateListener")) {
            return (IObstacleStateListener) code -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(code)));
            };
        } else if (paramName.contains("ICameraCaptureInfoListener")) {
            return (ICameraCaptureInfoListener) (cameraLensType, statusInfo) -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(statusInfo)));
            };
        } else if (paramName.contains("ICameraInformationListener")) {
            return (ICameraInformationListener) (cameraLensType, statusInfo) -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(statusInfo)));
            };
        } else if (paramName.contains("ICameraNewGeneratedInfoListener")) {
            return (ICameraNewGeneratedInfoListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("ICameraStorageInfoListener")) {
            return (ICameraStorageInfoListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        } else if (paramName.contains("IThermalAreaMeasureListener")) {
            return new IThermalAreaMeasureListener() {
                @Override
                public void onData(ThermalAreaMeasureInfo info) {
                    if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
                }

                @Override
                public void onResult(GEOAIError error) {

                }
            };
        } else if (paramName.contains("IThermalSpotMeasureListener")) {
            return new IThermalAreaMeasureListener() {
                @Override
                public void onData(ThermalAreaMeasureInfo info) {
                    if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
                }

                @Override
                public void onResult(GEOAIError error) {

                }
            };
        } else if (paramName.contains("IK3QHardwareStateListener")) {
            return new IK3QHardwareStateListener() {
                @Override
                public void onResult(K3QButtonHardwareInfo info) {
                    if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
                }
            };
        } else if (paramName.contains("ICellularVideoQualityListener")) {
            return new ICellularVideoQualityListener() {
                @Override
                public void onResult(CellularQualityInfo info) {
                    if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
                }
            };
        } else if (paramName.contains("IMissionStateListener")) {
            return new IMissionStateListener() {
                @Override
                public void onMissionState(MissionState state, int totalWaypoint, int currentWaypoint, int currentAction, boolean isReadyToMission) {
                    if (mCallback != null) mCallback.onResult("state: " + state.name() + "--current: " + currentWaypoint + "--total: " + totalWaypoint + "--action: " + currentAction + "--ready: " + isReadyToMission);
                }

                @Override
                public void onMissionStart() {

                }

                @Override
                public void onMissionFinish() {

                }
            };
        } else if (paramName.contains("ICellularSignalLevelListener")) {
            return new ICellularSignalLevelListener() {
                @Override
                public void onResult(CellularSignalInfo info) {
                    if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
                }
            };
        } else if (paramName.contains("IGpsSatelliteInfoListener")) {
            return (IGpsSatelliteInfoListener) info -> {
                if (mCallback != null) mCallback.onResult(format(mGson.toJson(info)));
            };
        }
        return null;
    }

    /**
     * 获取callback回调实例
     * @param paramName
     * @return
     */
    public Object getCallbackInvoke(Context context, String paramName) {
        if (paramName.equals(CompletionCallback.ICompletionCallback.class.getName())) {
            return (CompletionCallback.ICompletionCallback) geoaiError -> {
                if (mCallback != null) {
                    if (geoaiError == null) {
                        Toast.makeText(context, "success", Toast.LENGTH_SHORT).show();
                        mCallback.onResult("success.");
                    } else {
                        Toast.makeText(context, format(mGson.toJson(geoaiError)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(geoaiError)));
                    }
                }
            };
        } else if (paramName.equals(CompletionCallback.ICompletionCallbackWith.class.getName())) {
            return new CompletionCallback.ICompletionCallbackWith<Object>() {
                @Override
                public void onFailure(GEOAIError geoaiError) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(geoaiError)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(geoaiError)));
                    }
                }

                @Override
                public void onResult(Object o) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(o)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(o)));
                    }
                }
            };
        } else if (paramName.equals(CompletionCallback.ICompletionCallbackWithParam.class.getName())) {
            return new CompletionCallback.ICompletionCallbackWithParam() {
                @Override
                public void onFailure(GEOAIError geoaiError) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(geoaiError)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(geoaiError)));
                    }
                }

                @Override
                public void onResult(int i) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(i)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(i)));
                    }
                }
            };
        } else if (paramName.equals(CompletionCallback.ICompletionCallbackWithString.class.getName())) {
            return new CompletionCallback.ICompletionCallbackWithString() {
                @Override
                public void onFailure(GEOAIError geoaiError) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(geoaiError)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(geoaiError)));
                    }
                }

                @Override
                public void onResult(String result) {
                    if (mCallback != null) {
                        Toast.makeText(context, format(mGson.toJson(result)), Toast.LENGTH_SHORT).show();
                        mCallback.onResult(format(mGson.toJson(result)));
                    }
                }
            };
        }
        return null;
    }

    /**
     * 获取方法参数总量
     *
     * @param method method
     * @return param count
     */
    public int getMethodParamCount(@NonNull Method method) {
        return method.getParameterTypes().length;
    }

    /**
     * 获取方法最后一个参数是否为接口类型
     *
     * @param method method
     * @return isInterface
     */
    public boolean isEndParamInterface(Method method) {
        Class<?>[] typeParameters = method.getParameterTypes();
        int paramLen = typeParameters.length;
        if (paramLen == 0) return false;
        Class<?> parameterType = method.getParameterTypes()[paramLen - 1];
        return parameterType.isInterface();
    }

    public String getEndParamName(Method method) {
        Class<?>[] typeParameters = method.getParameterTypes();
        int paramLen = typeParameters.length;
        if (paramLen == 0) return "";
        Class<?> parameterType = method.getParameterTypes()[paramLen - 1];
        return parameterType.getName();
    }

    public Class<?> getFirstParam(Method method) {
        return method.getParameterTypes()[0];
    }

    public String format(String mJson){
        if (!isJsonFormatted) return mJson;
        StringBuilder soruce=new StringBuilder(mJson);
        if(mJson.equals("")){
            return null;
        }
        int offset=0;//目标字符串插入空格偏移量
        int bOffset=0;//空格偏移量
        for(int i=0;i<mJson.length();i++){
            char charAt=mJson.charAt(i);
            if(charAt=='{'||charAt=='['){
                bOffset+=4;
                soruce.insert(i+offset+1,"\n"+generateBlank(bOffset));
                offset+=(bOffset+1);
            }else if(charAt==','){
                soruce.insert(i+offset+1,"\n"+generateBlank(bOffset));
                offset+=(bOffset+1);
            }else if(charAt=='}'||charAt==']'){
                bOffset-=4;
                soruce.insert(i+offset,"\n"+generateBlank(bOffset));
                offset+=(bOffset+1);
            }
        }
        return soruce.toString();
    }

    private String generateBlank(int num){
        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0;i<num;i++){
            stringBuilder.append(" ");
        }
        return stringBuilder.toString();
    }

    public void setInvokeCallback(InvokeCallback callback) {
        this.mCallback = callback;
    }

    public interface InvokeCallback {
        void onResult(String s);
    }
}
